# coding=utf-8
import re
from datetime import datetime
import signal
import pyttsx3
import itertools
import atexit


class TTSEngine(object):
    def __init__(self):
        pass

    def say(self, text: str) -> None:
        pass

    def close(self) -> None:
        pass


class PyttsxEngine(TTSEngine):

    def __init__(self):
        super().__init__()
        conf: Config = Config()
        self._eg: pyttsx3.Engine = pyttsx3.init()
        self._eg_voices = self._eg.getProperty("voices")
        if len(self._eg_voices) == 0:
            raise Exception("Error: No Such TTS Engine!")
        self._eg.setProperty("rate", conf.read_speed)
        print("TTS engin initialize successful! ")

    def say(self, text: str) -> None:
        self._get_engine().say(text)
        self._get_engine().runAndWait()

    def close(self) -> None:
        self._get_engine().stop()

    def _get_engine(self) -> pyttsx3.Engine:
        return self._eg


class DefaultTTSEngine(PyttsxEngine):
    def __init__(self):
        super(DefaultTTSEngine, self).__init__()
        # print(super()._eg_voices[0].name) ## error
        print(self._eg_voices[0].name)


class ZiraTTSEngine(PyttsxEngine):
    def __init__(self) -> None:
        super(ZiraTTSEngine, self).__init__()
        for i in self._eg_voices:
            if "Zira" in i.name:
                self._eg.setProperty("voice", i.id)
                print(i.name)
                return None
        raise Exception("Error: No found Zira")


class HuihuiTTSEngine(PyttsxEngine):
    def __init__(self) -> None:
        super().__init__()
        for i in self._eg_voices:
            if "Huihui" in i.name:
                self._eg.setProperty("voice", i.id)
                print(i.name)
                return None
        raise Exception("Error: No found Huihui")


def tts_egine_factory(class_name: str) -> TTSEngine:
    try:
        tts_engine = globals()[class_name]()
    except Exception:
        raise Exception("Reflect TTS Engine Error: " + class_name)
    return tts_engine


class Config(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls.book_name: str = ''
            cls.log_name: str = './output/last_view.log'
            cls.start_line: int = 0
            cls.replace_conf: bool = False
            cls.print_line: bool = True
            cls.limit_line: int = -1
            cls.limit_time: int = -1
            cls.read_speed: int = 200
            cls.tts_class_name = "DefaultTTSEngine"
            cls.conf_file = "./resources/ApplicationConfig.txt"
            with open(cls.conf_file, 'r', encoding="utf-8") as f:
                while True:
                    line: str = f.readline().strip()
                    if '#' in line:
                        line = line[:line.index('#')]
                    if line is None or len(line) == 0:
                        break
                    kv: list = line.split("=")
                    if len(kv) < 2:
                        # raise RuntimeError("Config Error: " + kv)
                        continue
                    key: str = kv[0].strip()
                    value: str = ''.join(map(lambda s: s.strip(), kv[1:]))
                    if key == "read.book.path":
                        cls.book_name = value
                    elif key == 'log.output.path':
                        cls.log_name = value
                    elif key == "read.start.line":
                        cls.start_line = int(value)
                    elif key == "replace.config.start":
                        cls.replace_conf = bool(1 if value == 'True' else 0)
                    elif key == "read.limit.line":
                        cls.limit_line = int(value)
                    elif key == "read.limit.time":
                        cls.limit_time = int(value)
                    elif key == 'book.chapter.regex':
                        cls.chapter_regex = value
                    elif key == 'read.speed.rate':
                        cls.read_speed = int(value)
                    elif key == "tts.engine.class":
                        cls.tts_class_name = value
                    elif key == 'print.read.line':
                        cls.print_line = bool(1 if value == 'True' else 0)
        return cls._instance

    def replace_add_configuration(self, conf_name: str, conf_value: str, lines: list) -> list:
        search_regex: str = rf'^\s*{conf_name}\s*=\s*\S+\s*(?:#[\s\S]*)?$'
        replace_regex: str = rf'(?<={conf_name})\s*=[^#]*'
        replace_value: str = f'={conf_value}'
        for i in range(len(lines)):
            if re.search(search_regex, lines[i]):
                lines[i] = re.sub(replace_regex, replace_value, lines[i])
                return lines
        lines.append("".join([conf_name, replace_value]))
        return lines

    def reset_start_line(self, start_line: int) -> None:
        self.start_line = start_line
        lines: list[str]
        with open(self.conf_file, 'r', encoding='utf-8') as f:
            lines = f.readlines()
        self.replace_add_configuration("read.start.line", str(self.start_line), lines)
        with open(self.conf_file, 'w', encoding='utf-8') as f:
            f.writelines(lines)
        return

    def __init__(self):
        pass

# abandon
class Cache(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:

            pass
        else:
            return cls._instance

    def write(self):
        pass

    def read(self):
        pass




class ReadStory(object):
    def __init__(self):
        conf: Config = Config()
        self.current_chapter: str = ''
        self.current_line: int = 0;
        self.end_flag: bool = False
        self.chapter_regex: str = conf.chapter_regex
        self.tts_class_name: str = conf.tts_class_name
        self.book_name: str = conf.book_name
        self.log_name: str = conf.log_name
        self.is_print_line: bool = conf.print_line
        self.is_replace_conf: bool = conf.replace_conf
        self.limit_line: int = conf.limit_line
        self.limit_time: int = conf.limit_time
        self.start_line: int = conf.start_line
        self.tts_engine: TTSEngine = tts_egine_factory(self.tts_class_name)
        self.f = None

    def read_story_line(self) -> None:
        start_time: float = datetime.now().timestamp()
        self.tts_engine.say(self.book_name)
        try:
            self.f = open(self.book_name, 'r', encoding='utf-8')
        except Exception:
            raise Exception("File Error: " + self.book_name)
        for i in range(self.start_line - 1):  # skip start_line
            next_line = list(itertools.islice(self.f, 1))
            if not next_line:
                self.current_line = i
                self.__close_log("start_line more than the count of lines in the file")
                return
        self.current_line = self.start_line - 1
        while True:
            if 0 < self.limit_line <= (self.current_line + 1) - self.start_line:
                self.__close_log("The count of the read lines in the file is more than the limit_line")
                return
            if 0 < self.limit_time <= datetime.now().timestamp() - start_time:
                self.__close_log("The time spent reading aloud from the file is greater than the limit_time")
                return

            line: str = self.f.readline().strip()
            self.current_line += 1
            if line is None:
                self.__close_log("file read complete")
                return
            line = line.strip()
            if len(line) == 0:
                continue
            if re.search(self.chapter_regex, line) is not None:
                self.current_chapter = line
            if self.is_print_line:
                print('line ' + str(self.current_line) + ": ", end="")
            # print(line)
            for i_line in re.split(r'[。\.]', line):
                if self.is_print_line:
                    print(i_line)
                self.tts_engine.say(i_line)
                if self.end_flag:
                    self.__close_log("force out")
                    return

    def stop(self):
        self.end_flag = True

    def __close_log(self, exit_reason: str = ''):
        self.tts_engine.close()
        if self.f is not None:
            self.f.close()
        exit_information: str = f'''
Exit read_story Application:
Book Name: {self.book_name}
Exit datetime: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
Exit reason: {exit_reason}
Last read start line: {self.start_line}
Last read end line: {self.current_line}
Last read end chapter: {self.current_chapter}
==========================================
'''
        print(exit_information)
        print("log file: " + self.log_name)
        if self.is_replace_conf:
            Config().reset_start_line(self.current_line)
            print("Successfully replaces the value of read.start.line in the configuration file "
                  "with current number of lines")
        try:
            with open(self.log_name, "a+", encoding='utf-8') as log_f:
                log_f.write(exit_information)
            print("Success in record logs！")
        except Exception:
            print("Fail in record logs")
            print(Exception)


if __name__ == "__main__":
    # 可以在在代码里改设置，也可以在配置文件里改设置
    rs = ReadStory()
    signal.signal(signal.SIGINT, lambda signal, frame: rs.stop())
    rs.read_story_line()
